package com.barry.excel;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.barry.bean.OperationData;
import com.barry.dto.ReportConfig;
import com.barry.dto.ReportOutput;
import com.barry.generator.ReportAction;
import com.barry.utils.FileUtil;
import com.barry.utils.ZipUtil;
import org.apache.commons.compress.utils.Lists;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @ClassName ExcelReportActionJava
 * @Description TODO
 * @Author wangxuexing
 * @Date 2020/2/26 9:36
 * @Version 1.0
 */
public abstract class ExcelReportActionJava<T> implements ReportAction {
    private static final Lock lock = new ReentrantLock();
    /**
     * 一个excel文件限制
     */
    private static final int ONE_EXCEL_LIMIT = 300000;
    /**
     * 单页大小
     */
    private static final int ONE_PAGE_SIZE = 10000;
    @Override
    public ReportOutput generateReport(long reportId, ReportConfig config) throws Exception{
        int allCount = allRecordsCount();
        File targetFile;
        int excelCount = 1;
        if(allCount > ONE_EXCEL_LIMIT){
            if(allCount%ONE_EXCEL_LIMIT == 0){
                excelCount = allCount/ONE_EXCEL_LIMIT;
            } else {
                excelCount = allCount/ONE_EXCEL_LIMIT + 1;
            }
            targetFile = File.createTempFile(downloadFileName(), ".zip");
        } else {
            targetFile = File.createTempFile(downloadFileName(), ".xlsx");
        }

        if(excelCount == 1){
            genReport(targetFile, config, getPageNum(allCount));
        } else {
            List<File> fileList = Lists.newArrayList();
            for(int i = 0; i<excelCount; i++){
                int pageNum;
                if((i+1) == excelCount){
                    int lastPageCount = ONE_EXCEL_LIMIT-(ONE_PAGE_SIZE*(excelCount-1));
                    pageNum = getPageNum(lastPageCount);
                } else {
                    pageNum = getPageNum(ONE_EXCEL_LIMIT);
                }
                File excelFile = File.createTempFile(downloadFileName()+(i+1), ".xlsx");
                genReport(excelFile, config, pageNum);
                fileList.add(excelFile);
            }
            ZipUtil.zipFiles(fileList, targetFile);
        }

        //上传云存储
        String targetUrl = saveReport(targetFile);
        //返回云存储路径
        return new ReportOutput(targetUrl);
    }

    /**
     * 获取所有查询记录条数
     * @return
     */
    protected abstract int allRecordsCount();

    /**
     * 生成报表
     * @param targetFile
     * @param config
     * @param pageNum
     * @throws Exception
     */
    private void genReport(File targetFile, ReportConfig config, int pageNum) throws Exception{
        OutputStream outputStream = new FileOutputStream(targetFile);
        InputStream templateInputStream = FileUtil.getInputStream(config.templateName());
        ExcelWriter excelWriter = EasyExcel.write(outputStream, OperationData.class)
                .withTemplate(templateInputStream).needHead(false)
                /*.registerWriteHandler(new MyRowWriteHandler())*/.build();
        // 这里注意 如果同一个sheet只要创建一次
        WriteSheet writeSheet = EasyExcel.writerSheet(config.sheetName()).build();
        CountDownLatch latch = new CountDownLatch(pageNum);
        // 去调用写入,这里我调用了五次，实际使用时根据数据库分页的总的页数来
        for (int i = 0; i < pageNum; i++) {
            // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
            int currentPage = i;
            new Thread(()->{
                write(lock, getReportDataByPage(currentPage, ONE_PAGE_SIZE, 1000), excelWriter, writeSheet);
                latch.countDown();
            });
        }
        latch.await();
        //千万别忘记finish 会帮忙关闭流
        excelWriter.finish();
    }

    /**
     * 分页获取数据
     * @param currentPage
     * @param pageSize
     * @return
     */
    protected abstract List<T> getReportDataByPage(int currentPage, int pageSize, int count);

    /**
     * 获取页数
     * @param count
     * @return
     */
    private int getPageNum(int count){
        int pageNum;
        if(count%ONE_PAGE_SIZE == 0){
            pageNum = count/ONE_PAGE_SIZE;
        } else {
            pageNum = count/ONE_PAGE_SIZE + 1;
        }
        return pageNum;
    }

    private String saveReport(File file){

        file.deleteOnExit();
        return "";//TODO
    }

    /**
     * 线程安全写入数据
     * @param excelWriter
     * @param data
     * @param writeSheet
     * @param <T>
     */
    private static <T> void write(Lock lock, List<T> data, ExcelWriter excelWriter, WriteSheet writeSheet){
        lock.lock();
        try{
            excelWriter.write(data, writeSheet);
            data = null;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void postCheck(){}
}
